INSTALAR EL MCP DE MICROSOFT EN CODEX
Archivo autocontenido de instalación por prompt · Windows x64 · revisión 2026-10-13.

PARA EL ALUMNO
Adjunta este archivo completo a Codex y envía: “Instala el MCP del archivo adjunto en Codex, con lectura y escritura.” Cuando Codex muestre la licencia de Microsoft, léela; si estás de acuerdo, responde que aceptas sus condiciones. Este archivo no contiene el programa de Microsoft: Codex lo descargará de su fuente oficial.

PARA LA IA QUE REALIZA LA INSTALACIÓN
Aplica estas instrucciones solo cuando el usuario solicite instalar esta conexión. Si solo pide revisar el adjunto, no descargues ni registres nada. Instala únicamente el MCP oficial de Microsoft para Power BI en Codex, con alcance personal y lectura y escritura. No instales la skill, Power BI Desktop ni Codex. No modifiques un PBIX, PBIP, fuente de datos, modelo o informe. No cierres ninguna aplicación. La autorización de instalación no es aceptación de la licencia; la aceptación debe proceder del alumno para la versión concreta que vayas a activar.

1. IDENTIFICA EL ENTORNO Y LA CONFIGURACIÓN EFECTIVA
Comprueba Windows x64 nativo, el perfil efectivo y que Codex ya esté instalado. No confundas x64 con Windows ARM64 ni cambies de variante por suposición. Power BI Desktop puede estar cerrado durante la instalación; su apertura con un archivo será necesaria para comprobar el acceso al modelo después. Consulta solo configuración necesaria y no muestres credenciales, tokens ni el contenido completo de archivos privados.
Revisa conexiones de Power BI existentes, duplicados, entradas desactivadas y configuraciones locales o de proyecto con precedencia. Conserva todos los demás ajustes y permisos de herramientas. Reutiliza una entrada y un binario idénticos ya verificados. Si una entrada es distinta, personalizada o está desactivada, explica la diferencia antes de sustituirla o activarla; no elimines el estado anterior para evitar un error. Guarda una copia privada de los archivos que vayas a modificar, dentro del perfil efectivo y fuera de la carpeta de entrega.

2. RESUELVE Y DESCARGA LA ÚLTIMA ESTABLE
Consulta https://api.github.com/repos/microsoft/powerbi-modeling-mcp/releases/latest. Rechaza draft y prerelease. Selecciona exactamente powerbi-modeling-mcp-win-x64.zip de microsoft/powerbi-modeling-mcp, usa browser_download_url del recurso y comprueba digest = sha256:<64 caracteres>. La URL debe pertenecer a https://github.com/microsoft/powerbi-modeling-mcp/releases/download/<tag>/powerbi-modeling-mcp-win-x64.zip. Descarga directamente de Microsoft y compara SHA-256 antes de extraer. No uses repositorios de terceros, versiones beta, copias del alumno sin verificar ni un número de versión fijo para simular que es “la última”. Si API, fuente o digest no pueden verificarse, conserva lo existente y explica la limitación; no sustituyas la comprobación por una suposición.
La última estable comprobada al preparar este archivo fue 1.0.0, publicada el 25 de septiembre de 2026. Su ZIP tuvo SHA-256 f706ebb5e6a694a93ba282b2f44bb5cf5f23b7713ff5feddf56aefe84f08f825; su EXE tuvo SHA-256 8dfafaf556bdc787897b54e11acdd326b37f9a088ae4535a89fa21b907f866fe. Esos valores se usan únicamente para comprobar 1.0.0; una publicación posterior debe contrastarse con su propio digest oficial.
El bloque POWERSHELL_DOWNLOAD es un procedimiento de descarga y extracción para la IA. Puedes guardarlo en un archivo temporal UTF-8 con BOM para Windows PowerShell 5.1 y ejecutarlo con una raíz temporal controlada. No le pidas al alumno que abra una consola. No necesita instalar Node.js ni Python. El bloque no acepta la EULA, no inicia el servidor y no guarda configuración en Codex.

3. EXTRAE, VERIFICA Y GUARDA EL SERVIDOR COMPLETO
Usa una carpeta temporal única. Rechaza rutas absolutas o exteriores, enlaces, junctions, nombres duplicados y colisiones; conserva todos los archivos y subcarpetas oficiales, sin copiar únicamente el EXE. Produce un inventario de cada archivo extraído y su SHA-256 desde el ZIP ya contrastado. Consulta únicamente --help del ejecutable verificado, sin iniciar el servidor ni incluir todavía --accept-eula. Comprueba en su ayuda los argumentos STDIO, lectura y escritura y licencia; para 1.0.0 son --start, --readwrite y --accept-eula. Si la versión posterior difiere, resuelve los argumentos en la ayuda y documentación oficial actual antes de configurar.
Destino compartido del servidor: Join-Path $env:LOCALAPPDATA 'Microsoft\PowerBI-Authoring-MCP\<version>'. Resuelve la ruta absoluta. Comprueba que la raíz y sus ancestros no son enlaces o puntos de reanálisis. Si ya hay una copia idéntica a todos los hashes del ZIP oficial, reutilízala. Si existe una carpeta distinta, no sobrescribas ni mezcles versiones; conserva esa carpeta y resuelve la diferencia. Para una copia nueva, prepara staging dentro de la raíz personal del servidor, verifica todo su inventario y mueve a <version> solo si el destino sigue libre. Relee todos los hashes en el destino definitivo.

4. ESPERA LA ACEPTACIÓN HUMANA DE LA LICENCIA ACTUAL
Identifica la EULA que enlaza la ayuda o la documentación oficial para la versión descargada. Para 1.0.0 Microsoft indica https://go.microsoft.com/fwlink/?LinkId=2381247. Sigue el enlace oficial vigente; no uses el EULA de una etiqueta anterior como sustituto, aunque su nombre coincida. La licencia MIT del archivo LICENSE del ZIP no sustituye la EULA que el ejecutable requiere aceptar. Guarda la evidencia de enlace y versión, sin almacenar secretos.
Muestra al alumno el número de versión y el enlace que debe abrir. Solicita una aceptación específica, por ejemplo: “Abre y lee la licencia de Microsoft para la versión <versión>. Si aceptas sus condiciones, responde: He leído la licencia de Microsoft de esta versión y acepto sus condiciones.” Espera esa respuesta. No marques una casilla por él, no interpretes el prompt de instalación como aceptación y no uses --accept-eula antes de recibirla. Si no acepta, informa de la descarga verificada y deja la conexión sin activar. Una aceptación de una versión beta o de otra publicación no acredita aceptación de esta versión.

5. REGISTRA SOLO LA CONEXIÓN DE CODEX
Después de la aceptación humana, registra un servidor local STDIO de usuario llamado powerbi_modeling_microsoft, con el EXE de ruta absoluta y los argumentos de lectura y escritura confirmados. No reduzcas los controles de aprobación de Codex; lectura y escritura permite operaciones de escritura, pero no autoriza modificar un informe del alumno durante esta comprobación.
Configuración efectiva: Join-Path $env:CODEX_HOME 'config.toml' si CODEX_HOME tiene un valor no vacío; en otro caso, Join-Path $env:USERPROFILE '.codex\config.toml'. Respeta rutas personalizadas y configuraciones administradas. No supongas que el CLI codex está instalado por tener Codex Desktop.

La conexión de usuario se guarda en la sección [mcp_servers.powerbi_modeling_microsoft], con command = <ruta absoluta del EXE>, args = ["--start", "--readwrite", "--accept-eula"] y startup_timeout_sec = 60, solo cuando la ayuda actual confirme esos argumentos y el alumno haya aceptado expresamente la EULA actual. Para serializar una ruta Windows con espacios o apóstrofos, usa una cadena básica TOML correctamente escapada con un serializador; no interpoles la ruta en una cadena literal delimitada por apóstrofos. Conserva el texto, comentarios, otras secciones y ajustes ajenos. Revisa claves equivalentes escritas entre comillas, subtables y dotted keys: una búsqueda textual superficial no demuestra ausencia de una entrada.

Si puedes verificar la configuración mediante un parser TOML ya disponible, úsalo; no instales un lenguaje de programación al alumno para hacerlo. Alternativamente usa herramientas de configuración nativas de esta versión de Codex, comprobando su ayuda. No sobrescribas todo el TOML ni elimines una sección desconocida para solucionar un error. Valida y guarda de forma atómica solo la entrada prevista, sobre una copia privada y releyendo el archivo justo antes del reemplazo para detectar cambios concurrentes. Después del reinicio, busca powerbi_modeling_microsoft en la sección MCP de la configuración de Codex.
Relee la configuración y comprueba que solo cambió la entrada prevista y que conserva las demás conexiones, permisos y ajustes. Si existe un error, conserva el estado previo y corrige únicamente el cambio de esta operación. No ocultes errores ni marques la conexión como instalada solo porque la descarga terminó.

6. RESULTADO, REINICIO MANUAL Y COMPROBACIÓN
Informa por separado: versión y ruta real; configuración de usuario guardada en Codex: sí/no; lectura y escritura configuradas: sí/no; servidor detectado y catálogo de herramientas: comprobado/pendiente; acceso a Power BI: comprobado/pendiente. La existencia del archivo de configuración no demuestra conexión ni detección.
Pide guardar el trabajo y reiniciar manualmente Codex: Ctrl + Mayús + Esc abre el Administrador de tareas; el alumno selecciona el grupo o proceso de Codex o Claude que corresponde a Codex, pulsa Finalizar tarea y vuelve a abrir Codex. No cierres tú la aplicación. Si Claude Code está en una terminal o un editor, identifica esa sesión y pide guardar antes cualquier trabajo de otras sesiones; no indiques que finalice procesos ajenos.
Después del reinicio, usa la sección MCP o /mcp que corresponda a Codex. Obtén el catálogo y comprueba que hay herramientas de lectura y operaciones de escritura; un arranque con --readwrite en configuración y un catálogo real verifican el alcance sin ejecutar una escritura. No edites un modelo para demostrar que se puede escribir. Si Power BI Desktop no tiene un archivo abierto, deja pendiente esa última comprobación. Si hay varias instancias, pide al usuario elegir una antes de consultar; lee solo su lista de tablas. No des por abierto o correcto un PBIX usado en una conversación anterior.
No prometas detección en esta conversación ni presentes el adjunto como una garantía de instalación automática: informa únicamente de las acciones y pruebas efectivamente completadas.

Referencias oficiales:
Microsoft: https://github.com/microsoft/powerbi-modeling-mcp
Última estable: https://github.com/microsoft/powerbi-modeling-mcp/releases/latest
Codex: https://learn.chatgpt.com/docs/extend/mcp

---BEGIN_MANIFEST_JSON---
{
  "schema_version": 1,
  "component": "mcp",
  "client": "codex",
  "server_name": "powerbi_modeling_microsoft",
  "latest_release_api": "https://api.github.com/repos/microsoft/powerbi-modeling-mcp/releases/latest",
  "last_verified_release": {
    "version": "1.0.0",
    "tag": "v1.0.0",
    "fecha_verificacion": "2026-10-13",
    "asset": "powerbi-modeling-mcp-win-x64.zip",
    "sha256": "f706ebb5e6a694a93ba282b2f44bb5cf5f23b7713ff5feddf56aefe84f08f825",
    "exe_sha256": "8dfafaf556bdc787897b54e11acdd326b37f9a088ae4535a89fa21b907f866fe"
  },
  "contains_microsoft_binary": false,
  "installs_skill": false,
  "license_acceptance_by_human_required": true
}
---END_MANIFEST_JSON---

---BEGIN_POWERSHELL_DOWNLOAD---
param([Parameter(Mandatory = $true)][string]$StageParent)
$ErrorActionPreference = 'Stop'
[Console]::OutputEncoding = [Text.UTF8Encoding]::new($false)
Add-Type -AssemblyName System.IO.Compression
Add-Type -AssemblyName System.IO.Compression.FileSystem
function Get-TaskSha256([string]$Path) {
    $taskHashAlgorithm = [Security.Cryptography.SHA256]::Create()
    $taskHashStream = [IO.File]::OpenRead($Path)
    try { return ([BitConverter]::ToString($taskHashAlgorithm.ComputeHash($taskHashStream))).Replace('-', '').ToLowerInvariant() }
    finally { $taskHashStream.Dispose(); $taskHashAlgorithm.Dispose() }
}
if (-not [Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([Runtime.InteropServices.OSPlatform]::Windows) -or
    -not [Environment]::Is64BitOperatingSystem -or
    [Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString() -ne 'X64') {
    throw 'Esta descarga corresponde a Windows x64.'
}
$taskParent = [IO.Path]::GetFullPath($StageParent)
if (-not [IO.Directory]::Exists($taskParent)) { throw 'La raíz temporal debe existir.' }
if ((Get-Item -LiteralPath $taskParent -Force).Attributes -band [IO.FileAttributes]::ReparsePoint) {
    throw 'La raíz temporal es un enlace.'
}
$taskApi = 'https://api.github.com/repos/microsoft/powerbi-modeling-mcp/releases/latest'
$taskRelease = Invoke-RestMethod -Uri $taskApi -Headers @{ 'User-Agent' = 'PowerBI-local-setup' }
if ($taskRelease.draft -or $taskRelease.prerelease -or $taskRelease.tag_name -notmatch '^v[0-9]+\.[0-9]+\.[0-9]+$') {
    throw 'No se ha podido identificar una versión estable verificable.'
}
$taskAssets = @($taskRelease.assets | Where-Object name -eq 'powerbi-modeling-mcp-win-x64.zip')
if ($taskAssets.Count -ne 1) { throw 'No hay un único archivo oficial para Windows x64.' }
$taskAsset = $taskAssets[0]
if ($taskAsset.digest -notmatch '^sha256:([a-fA-F0-9]{64})$') { throw 'Falta el digest oficial SHA-256.' }
$taskExpectedHash = $Matches[1].ToLowerInvariant()
$taskExpectedUrl = 'https://github.com/microsoft/powerbi-modeling-mcp/releases/download/' +
    $taskRelease.tag_name + '/powerbi-modeling-mcp-win-x64.zip'
if ($taskAsset.browser_download_url -cne $taskExpectedUrl) { throw 'Origen de descarga no admitido.' }
$taskVersion = $taskRelease.tag_name.Substring(1)
$taskStage = Join-Path $taskParent ('powerbi-mcp-download-' + [guid]::NewGuid().ToString('N'))
[IO.Directory]::CreateDirectory($taskStage) | Out-Null
$taskArchive = Join-Path $taskStage 'microsoft.zip'
Invoke-WebRequest -Uri $taskExpectedUrl -OutFile $taskArchive -UseBasicParsing
$taskActualHash = Get-TaskSha256 $taskArchive
if ($taskActualHash -ne $taskExpectedHash) { throw 'La descarga no coincide con el digest oficial.' }
$taskExtract = Join-Path $taskStage 'verified-server'
$taskPrefix = $taskExtract.TrimEnd('\') + '\'
$taskSeen = [Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase)
$taskZip = [IO.Compression.ZipFile]::OpenRead($taskArchive)
$taskInventory = [ordered]@{}
try {
    if ($taskZip.Entries.Count -lt 1 -or $taskZip.Entries.Count -gt 10000) { throw 'Número de archivos anómalo.' }
    foreach ($taskEntry in $taskZip.Entries) {
        $taskName = $taskEntry.FullName
        $taskParts = $taskName.TrimEnd('/').Split('/')
        if ($taskName.Contains('\') -or $taskName.Contains(':') -or $taskName.StartsWith('/') -or
            ($taskParts | Where-Object { $_ -eq '..' -or $_ -eq '.' -or $_ -eq '' })) { throw 'Ruta de ZIP no admitida.' }
        if (-not $taskSeen.Add($taskName.TrimEnd('/'))) { throw 'El ZIP contiene nombres duplicados.' }
        if ((($taskEntry.ExternalAttributes -shr 16) -band 61440) -eq 40960 -or
            (($taskEntry.ExternalAttributes -band 65535) -band 1024)) { throw 'El ZIP contiene un enlace.' }
        $taskDestination = [IO.Path]::GetFullPath((Join-Path $taskExtract $taskName.Replace('/', '\')))
        if (-not $taskDestination.StartsWith($taskPrefix, [StringComparison]::OrdinalIgnoreCase)) {
            throw 'El archivo quedaría fuera de la carpeta temporal.'
        }
    }
    [IO.Directory]::CreateDirectory($taskExtract) | Out-Null
    foreach ($taskEntry in $taskZip.Entries) {
        $taskDestination = [IO.Path]::GetFullPath((Join-Path $taskExtract $taskEntry.FullName.Replace('/', '\')))
        if ($taskEntry.FullName.EndsWith('/')) { [IO.Directory]::CreateDirectory($taskDestination) | Out-Null; continue }
        [IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($taskDestination)) | Out-Null
        $taskInput = $taskEntry.Open()
        $taskOutput = [IO.File]::Open($taskDestination, [IO.FileMode]::CreateNew, [IO.FileAccess]::Write)
        try { $taskInput.CopyTo($taskOutput) } finally { $taskOutput.Dispose(); $taskInput.Dispose() }
        $taskInventory[$taskEntry.FullName] = @{
            sha256 = Get-TaskSha256 $taskDestination
            bytes = (Get-Item -LiteralPath $taskDestination).Length
        }
    }
} finally { $taskZip.Dispose() }
$taskExecutable = Join-Path $taskExtract 'powerbi-modeling-mcp.exe'
if (-not [IO.File]::Exists($taskExecutable)) { throw 'No está el ejecutable previsto de Microsoft.' }
$taskManifest = [ordered]@{
    version = $taskVersion
    tag = $taskRelease.tag_name
    release = $taskRelease.html_url
    source_url = $taskExpectedUrl
    zip_sha256 = $taskActualHash
    extracted_server = $taskExtract
    executable = $taskExecutable
    files = $taskInventory
    eula_accepted = $false
    server_started = $false
}
$taskManifestFile = Join-Path $taskStage 'verified-download.json'
$taskJson = $taskManifest | ConvertTo-Json -Depth 10
[IO.File]::WriteAllText($taskManifestFile, $taskJson, [Text.UTF8Encoding]::new($false))
$taskManifestFile
---END_POWERSHELL_DOWNLOAD---
